<template>
    <div>
        <div class="component-upload-image">
            <!-- 上传组件 -->
            <el-upload multiple :action="uploadImgUrl" list-type="picture-card" :before-upload="handleBeforeUpload"
                :limit="limit" :on-exceed="handleExceed" :headers="headers" :file-list="fileList" :show-file-list="false"
                :disabled="disabled" :class="{ hide: fileList.length >= limit }">
                <el-icon class="avatar-uploader-icon">
                    <Plus />
                </el-icon>
            </el-upload>

            <!-- 图片预览列表 -->
            <div v-for="(item, index) in fileList" :key="index" class="preview-item">
                <img :src="item.url" class="preview-image" @click="handlePictureCardPreview(item)">
                <div class="actions">
                    <!-- <el-icon class="action-icon" @click="handleEdit(index)">
                        <Edit />
                    </el-icon> -->
                    <el-icon class="action-icon" @click="handleRemove(index)">
                        <Close />
                    </el-icon>
                </div>
            </div>



            <!-- 裁剪对话框 -->
            <el-dialog :title="isEditing ? '编辑图片' : '图片裁剪'" v-model="cropVisible" width="70vw" append-to-body
                @closed="handleCropClose">
                {{ ShowTip }}
                <el-row>
                    <el-col :span="24" :style="{ height: '600px' }">
                        <vue-cropper ref="cropperRef" :img="cropData.url" :output-size="cropData.outputSize"
                            :output-type="cropData.outputType" :info="true" :auto-crop="cropData.autoCrop"
                            :fixed="cropData.fixed" :fixed-number="cropData.fixedNumber" @realTime="handleCropPreview" />
                    </el-col>
                    <!-- <el-col :span="12" :style="{ height: '400px' }">
                    <div class="preview-wrapper">
                        <div class="preview-image" :style="{
                            width: previews.w + 'px',
                            height: previews.h + 'px',
                            overflow: 'hidden',
                            margin: '0 auto'
                        }">
                            <img :src="previews.url" :style="previews.img" />
                        </div>
                    </div>
                </el-col> -->
                </el-row>

                <template #footer>
                    <div class="dialog-footer">
                        <el-button @click="cropVisible = false">取 消</el-button>
                        <el-button type="primary" @click="handleCropComplete">确 定</el-button>
                    </div>
                </template>
            </el-dialog>

            <!-- 图片预览 -->
            <el-image-viewer v-if="previewVisible" :url-list="[previewImage]" @close="previewVisible = false" />
        </div>
        <!-- 上传提示 -->
        <div class="el-upload__tip" v-if="showTip">
            请上传
            <template v-if="fileSize">
                大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
            </template>
            <template v-if="fileType">
                格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
            </template>
            最多上传 {{ limit }}张
            {{ ShowTip }}
            的文件
        </div>
    </div>
</template>

<script setup>
import { ref, computed, watch, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus';
import axios from "axios";
import { getToken } from "@/utils/auth";
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import Compressor from "compressorjs";
const { proxy } = getCurrentInstance()

const props = defineProps({
    modelValue: [String, Array, Object],
    limit: {
        type: Number,
        default: 1
    },
    fileSize: {
        type: Number,
        default: 10
    },
    fileType: {
        type: Array,
        default: () => ['jpg', 'png', 'jpeg']
    },
    // 是否开启裁剪
    crop: {
        type: Boolean,
        default: false
    },
    // 裁剪比例 [宽, 高]
    cropRatio: {
        type: Array,
        default: () => [16, 9]
    },
    compressionRatio: {
        type: Number,
        default: 0.7
    },
    disabled: Boolean,
    ShowTip: {
        type: String,
        default: '',
    },
})

const emit = defineEmits(['update:modelValue'])

// 上传相关
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + '/common/ossUploadFile')
const headers = ref({ Authorization: 'Bearer ' + getToken() })
const fileList = ref([])

// 裁剪相关
const cropperRef = ref(null)
const cropVisible = ref(false)
const cropData = ref({
    url: '',
    outputSize: 1,
    outputType: 'png',
    autoCrop: true,
    fixed: false,
    fixedNumber: props.cropRatio
})
const previews = ref({})
const isEditing = ref(false)
const editIndex = ref(-1)

// 预览相关
const previewVisible = ref(false)
const previewImage = ref('')

// 计算属性
const showTip = computed(() => props.fileSize || props.fileType?.length)

watch(() => props.modelValue, value => {
    if (value) {
        fileList.value = Array.isArray(value)
            ? value.map(normalizeFileItem)
            : value.split(',').map(normalizeFileItem)
    } else {
        fileList.value = []
    }
}, { immediate: true, deep: true })

function normalizeFileItem(item) {
    if (typeof item === 'string') {
        return { url: item, name: item.split('/').pop() }
    }
    return item
}

async function handleBeforeUpload(file) {
    // 文件类型校验
    const fileExtension = file.name.split('.').pop().toLowerCase()
    if (!props.fileType.includes(fileExtension)) {
        ElMessage.error(`文件格式错误，仅支持 ${props.fileType.join('/')} 格式`)
        return false
    }

    // 文件大小校验
    if (file.size > props.fileSize * 1024 * 1024) {
        ElMessage.error(`文件大小不能超过 ${props.fileSize}MB`)
        return false
    }

    // 需要裁剪时进入裁剪流程
    if (props.crop) {
        openCropper(file)
        return false
    }

    // 不需要裁剪时直接处理压缩
    return props.compressionRatio < 1
        ? await compressFile(file)
        : file
}

function openCropper(file) {
    const reader = new FileReader()
    reader.onload = e => {
        cropData.value.url = e.target.result
        cropVisible.value = true
    }
    reader.readAsDataURL(file)
}
/**
 * 生成优化文件名
 * @param {string} originalName - 原始文件名
 * @param {string} mimeType - 文件MIME类型
 * @returns {string} 优化后的文件名
 */
function generateFileName(originalName, mimeType) {
    // 1. 清理特殊字符
    const cleanName = originalName.replace(/[^a-zA-Z0-9_\u4e00-\u9fa5.-]/g, '');

    // 2. 分离名称和扩展名
    const extMatch = cleanName.match(/\.([0-9a-z]+$)/i);
    const [name, originalExt] = extMatch
        ? [cleanName.slice(0, -extMatch[0].length), extMatch[1]]
        : [cleanName, ''];

    // 3. 根据MIME类型获取正确扩展名
    const mimeExt = getExtensionFromMime(mimeType);
    const finalExt = mimeExt || originalExt || '';

    // 4. 添加时间戳
    const timestamp = Date.now().toString().slice(-6);

    // 5. 组合最终文件名
    return `${name}_${timestamp}${finalExt ? '.' + finalExt : ''}`;
}

/**
 * 根据MIME类型获取文件扩展名
 */
function getExtensionFromMime(mimeType) {
    const mimeMap = {
        'image/jpeg': 'jpg',
        'image/png': 'png',
        'image/gif': 'gif',
        'image/webp': 'webp'
    };
    return mimeMap[mimeType] || '';
}

async function handleCropComplete() {
    proxy.$refs.cropperRef.getCropBlob(async blob => {
        console.log("blob", blob)
        // 获取原始文件信息（编辑模式）
        const originalFile = isEditing.value
            ? fileList.value[editIndex.value]?.raw
            : null;
        console.log("originalFile", originalFile)
        // 生成优化文件名
        const fileName = generateFileName(
            originalFile?.name || `cropped`,
            blob.type
        );
        console.log("fileName", fileName)

        const file = new File([blob], fileName, {
            type: blob.type,
            lastModified: Date.now()
        })
        console.log("file", file)

        // 压缩处理
        const finalFile = props.compressionRatio < 1
            ? await compressFile(file)
            : file

        // 更新文件列表
        if (isEditing.value) {
            fileList.value[editIndex.value] = await uploadFile(finalFile)
        } else {
            fileList.value.push(await uploadFile(finalFile))
        }

        updateModelValue()
        cropVisible.value = false
    });
    // cropperRef.value.getCroppedCanvas().toBlob(async blob => {
    //     const file = new File([blob], `cropped_${Date.now()}`, {
    //         type: blob.type,
    //         lastModified: Date.now()
    //     })

    //     // 压缩处理
    //     const finalFile = props.compressionRatio < 1
    //         ? await compressFile(file)
    //         : file

    //     // 更新文件列表
    //     if (isEditing.value) {
    //         fileList.value[editIndex.value] = await uploadFile(finalFile)
    //     } else {
    //         fileList.value.push(await uploadFile(finalFile))
    //     }

    //     updateModelValue()
    //     cropVisible.value = false
    // })
}

async function compressFile(file) {
    return new Promise((resolve, reject) => {
        new Compressor(file, {
            quality: props.compressionRatio,
            success: resolve,
            error: reject
        })
    })
}

async function uploadFile(file) {
    const formData = new FormData()
    formData.append('file', file, file.name)

    try {
        const res = await axios.post(uploadImgUrl.value, formData, { headers: headers.value })
        console.log("res", res)
        if (res.data.code === 200) {
            return { url: res.data.data, name: file.name, raw: file }
        }
        // throw new Error(res.message || '上传失败')
    } catch (error) {
        ElMessage.error(error.message)
        throw error
    }
}

async function handleEdit(index) {
    isEditing.value = true
    editIndex.value = index
    // console.log("fileList.value[index]", urlToFile(fileList.value[index].url, fileList.value[index].name))
    let file = await urlToFile(fileList.value[index].url, fileList.value[index].name)
    console.log("file", file)
    openCropper(file || fileList.value[index].raw)
}
/**
 * 将URL转换为File对象
 * @param {string} url - 可以是网络URL或DataURL
 * @param {string} filename - 文件名（带扩展名）
 * @param {string} [mimeType] - 可选MIME类型，默认自动推断
 * @returns {Promise<File>} 
 */
async function urlToFile(url, filename, mimeType) {
    try {
        // 处理DataURL格式（如：'data:image/png;base64,...'）
        if (url.startsWith('data:')) {
            const [meta, data] = url.split(',');
            const [, type = 'application/octet-stream', encoding] = meta.split(';');
            const mime = type.split(':')[1] || mimeType || 'application/octet-stream';

            // 处理Base64编码
            if (encoding === 'base64') {
                const byteString = atob(data);
                const buffer = new ArrayBuffer(byteString.length);
                const array = new Uint8Array(buffer);
                for (let i = 0; i < byteString.length; i++) {
                    array[i] = byteString.charCodeAt(i);
                }
                return new File([buffer], filename, { type: mime });
            }

            // 处理普通URL编码
            return new File([decodeURIComponent(data)], filename, { type: mime });
        }

        // 处理网络URL
        const response = await fetch(url);
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        const blob = await response.blob();
        const detectedMime = mimeType || blob.type || 'application/octet-stream';

        return new File([blob], filename, { type: detectedMime });
    } catch (error) {
        console.error('Failed to convert URL to File:', error);
        throw error; // 可选择重新抛出错误或在外部处理
    }
}
function handleRemove(index) {
    fileList.value.splice(index, 1)
    updateModelValue()
}

function handleCropPreview(data) {
    previews.value = data
}

function handleCropClose() {
    cropData.value.url = ''
    isEditing.value = false
    editIndex.value = -1
}
function listToString(list, separator) {
    let strs = "";
    separator = separator || ",";
    for (let i in list) {
        if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
            strs += list[i].url.replace(baseUrl, "") + separator;
        }
    }
    return strs != "" ? strs.substr(0, strs.length - 1) : "";
}
function updateModelValue() {
    console.log("fileList.value", fileList.value)
    // const value = fileList.value.map(item => item.url)
    console.log("listToString(fileList.value)", listToString(fileList.value))
    emit('update:modelValue', props.limit === 1 ? listToString(fileList.value) : listToString(fileList.value))
}

function handlePictureCardPreview(item) {
    previewImage.value = item.url
    previewVisible.value = true
}

function handleExceed() {
    ElMessage.warning(`最多上传 ${props.limit} 个文件`)
}
</script>

<style scoped>
.component-upload-image {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.preview-item {
    position: relative;
    width: 148px;
    height: 148px;
    border: 1px solid #ebeef5;
    border-radius: 6px;
    overflow: hidden;

    &:hover .actions {
        opacity: 1;
    }
}

.preview-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.actions {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 20px;
    opacity: 0;
    transition: opacity 0.3s;
}

.action-icon {
    color: #fff;
    font-size: 24px;
    cursor: pointer;
    transition: color 0.3s;

    &:hover {
        color: #409eff;
    }
}

:deep(.el-upload--picture-card) {
    width: 148px;
    height: 148px;
}

.hide :deep(.el-upload--picture-card) {
    display: none;
}

.preview-wrapper {
    height: 400px;
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>